import math;

class ActGroup(object):
    def __init__(self, sTime, graph):
        self.aActSubj = [];
        self.aSubjects = [];
        self.sTime = sTime;
        self.mSubj2States = dict();
        self.mSubj2StNames = dict();
        self.aPastActGrps = [];
        self.mPast_Subj2States = dict();
        self.aPastSubjects = [];
        self.graph = graph;
        self.aJntFwd = dict(); # tuple(aSubjStates) -> nJntFwd
        self.aMaxFwd_PastInds = dict(); # tuple(aSubjStates) -> aMaxFwd_PastStates_Inds
        self.aBest_Subj2States = dict();
        self.aBase = [];
        self.nCombinations = 1;
        
    def add(self, actSubj):
        if not(actSubj in self.aActSubj):
            self.aActSubj.append(actSubj);
            self.aSubjects.append(actSubj.sArgSubject);
            aNames = [];
            for node in actSubj.aStates:
                aNames.append(node.rootAct.name);
            self.mSubj2States[actSubj.sArgSubject] = actSubj.aStates;
            self.mSubj2StNames[actSubj.sArgSubject] = aNames;
            self.aBase.append(len(actSubj.aStates));
            self.nCombinations *= self.aBase[-1];
                
    def setPastActGrps(self, aPastActGrps):
        self.aPastActGrps = aPastActGrps;        
        for subject in self.aSubjects:
            bSubjFound = False;
            for pastActGrp in self.aPastActGrps:
                if subject in pastActGrp.aSubjects:
                    bSubjFound = True;
                    break;
            if bSubjFound:
                self.aPastSubjects.append(subject);
        
    def getMarginal_PastFwd(self, aSubjects, aSubjStates):
        aSub_Subjects, aSub_SubjStates = [], [];
        aSubjMatch_Inds, aSubjNonMatch_Inds = [], [];
        #Get Subset of Subjects
        for i,subject in enumerate(self.aSubjects):
            if subject in aSubjects:
                aSub_Subjects.append(subject);
                iSubj = aSubjects.index(subject);
                aSub_SubjStates.append(aSubjStates[iSubj]);
                aSubjMatch_Inds.append(i);
            else:
                aSubjNonMatch_Inds.append(i);
        #Marginalize JntFwd to get Requested Subset
        nCombinations_NonMatch, aSubBase = 1, [];
        aFull_SubjStates = [0]*len(self.aSubjects);
        nMarginal_JntFwd = 0; #1;
        for i,iMatch in enumerate(aSubjMatch_Inds): #Set Fixed States of Subjects Found
            aFull_SubjStates[iMatch] = aSub_SubjStates[i];
        for iNonMatch in aSubjNonMatch_Inds: #Get Bases & Number of Combinations (from Subjects NOT Found)
            nCombinations_NonMatch *= self.aBase[iNonMatch];
            aSubBase.append(self.aBase[iNonMatch]);
        for i in range(nCombinations_NonMatch):
            aSubStates_Inds = self.getPermutation(i, aSubBase); #Permutate over Subject's States NOT Found
            for i,iNonMatch in enumerate(aSubjNonMatch_Inds): 
                aFull_SubjStates[iNonMatch] = aSubStates_Inds[i]; #Fill the Permutated States (for Subjects NOT Found)
            if self.check_Inconsistency(aFull_SubjStates): continue; #Avoid Invalid Permutations in Marginalization
#             [nJntFwd, aMaxFwd_Inds] = self.getJntFwd(aFull_SubjStates);  # @UnusedVariable
            nJntFwd = self.aJntFwd[tuple(aFull_SubjStates)];
#             nMarginal_JntFwd += nJntFwd;
#             return nMarginal_JntFwd;
            nMarginal_JntFwd += math.exp(nJntFwd); #*= math.exp(nJntFwd);
        nMarginal_JntFwd = math.log(nMarginal_JntFwd) if nMarginal_JntFwd>0 else float("-inf"); #Log-Likelihoods
        return nMarginal_JntFwd;            
        
    def getAll_JntFwds(self):
        aSubjStates = [];
        aConsistentSt = self.getConsistentStates();
        print len(aConsistentSt);
        for aSubjStates in aConsistentSt: #range(self.nCombinations):
#             aSubjStates = self.getPermutation(i, self.aBase);
            [nJntFwd, aMaxFwd_PastStates_Inds] = self.getJntFwd(aSubjStates);
            self.aJntFwd[tuple(aSubjStates)] = nJntFwd;
            self.aMaxFwd_PastInds[tuple(aSubjStates)] = aMaxFwd_PastStates_Inds;
#             print self.aJntFwd, aSubjStates;
        print self.aSubjects;
        return [self.aJntFwd, self.aMaxFwd_PastInds];
        
    def getJntFwd(self, aSubjStates):
        if self.check_Inconsistency(aSubjStates):
#             nJntFwd = float("-inf"); #0;
#             aMaxFwd_PastStates_Inds = [0]*
            return [float("-inf"), []];
        aStates = [];
        for i,subject in enumerate(self.aSubjects):
            aStates.append(self.mSubj2States[subject][aSubjStates[i]]);
        #Get Product of Observations        
        nObsProb = 0; #1;
        for actM in aStates:
#             nObsProb *= actM.rootAct.aScores[0]; #Likelihoods
            nScore = actM.rootAct.aScores[0];
            nScore = math.log(nScore) if nScore>0 else float("-inf"); #Log-Likelihoods
            nObsProb += nScore;
        #Get Max (Trans * PastFwd)
        [nMax_TransFwd, aMaxFwd_PastStates_Inds] = self.getMax_TransFwd(aSubjStates);
#         nJntFwd = nObsProb * nMax_TransFwd;
        nJntFwd = nObsProb + nMax_TransFwd;
        return [nJntFwd, aMaxFwd_PastStates_Inds];
    
    def check_Inconsistency(self, aSubjStates):
        for i,subject in enumerate(self.aSubjects): #For each Subject, get their candidate State (to check Consistency)
            iState = aSubjStates[i];
            stNode = self.mSubj2States[subject][iState].rootAct; #Get its Candidate State Node
            if len(stNode.aOnlySubjects)>1: #If it involves multiple subjects, then make sure the others have same State
                for otherSubject in stNode.aOnlySubjects:
                    if subject==otherSubject: continue;
                    iSubj = self.aSubjects.index(otherSubject);
                    iOtherState = aSubjStates[iSubj];
                    stOtherNode = self.mSubj2States[otherSubject][iOtherState].rootAct; #Get Other Candidate State Nodes
                    if stOtherNode!=stNode:
                        return True;
        return False;
    
    def check_Sub_Inconsistency(self, aSubjects, aSubjStates):
        for i,subject in enumerate(aSubjects): #For each Subject, get their candidate State (to check Consistency)
            iState = aSubjStates[i];
            stNode = self.mSubj2States[subject][iState].rootAct; #Get its Candidate State Node
            if len(stNode.aOnlySubjects)>1: #If it involves multiple subjects, then make sure the others have same State
                for otherSubject in stNode.aOnlySubjects:
                    if subject==otherSubject: continue;
                    iSubj = aSubjects.index(otherSubject);
                    iOtherState = aSubjStates[iSubj];
                    stOtherNode = self.mSubj2States[otherSubject][iOtherState].rootAct; #Get Other Candidate State Nodes
                    if stOtherNode!=stNode:
                        return True;
        return False;
    
    def getBest_ConsistentStates(self, aNext_actGrps): #Fix Inconsistencies with this function 
        nMax_TransFwd = float("-inf");
        for aSubjStates in self.aJntFwd: #For each Consistent Candidate
            nFwd = self.aJntFwd[tuple(aSubjStates)];
            if nFwd==float("-inf"): continue;
            nTransProd = 0;
            for next_actGrp in aNext_actGrps: #For each next_actGrp (Get their nTransProd)
                aSubjects, aStates_Inds, aPastSubj, aPastStates_Inds = [], [], [], [];
                for nextSubj in next_actGrp.aSubjects: #Collect Subjects & States involved between this_Grp & next_actGrp
                    if nextSubj in self.aSubjects:
                        iSubj = self.aSubjects.index(nextSubj);
                        aPastSubj.append(self.aSubjects[iSubj]);
                        aPastStates_Inds.append(aSubjStates[iSubj]);
                        aSubjects.append(nextSubj);
                        aStates_Inds.append(next_actGrp.aBest_Subj2States[nextSubj]);
                nTransProd += next_actGrp.getTransProd(aSubjects, aStates_Inds, aPastSubj, aPastStates_Inds);
            nTransFwd = nTransProd + nFwd;
            if nTransFwd>nMax_TransFwd:
                nMax_TransFwd = nTransFwd;
                aMaxFwd_States_Inds = aSubjStates;
        self.setAll_BestStates(aMaxFwd_States_Inds);
#         for i,subject in enumerate(self.aSubjects):
#             self.aBest_Subj2States[subject] = aMaxFwd_States_Inds[i];
        return self.aBest_Subj2States;
    
    def getMax_TransFwd(self, aStates_Inds):
        nMax_TransFwd, aMaxFwd_PastStates_Inds = float("-inf"), [];
        nPastCombs, aPastBase = 1, [];
        self.mPast_Subj2States = dict();
        aPastSubjects = self.aPastSubjects;
        #Function to get all Past States (involving the given Subject's Name)
        def getPastStates(sPastSubject):
            pastStates = [];
            for pastActGrp in self.aPastActGrps:
                if sPastSubject in pastActGrp.aSubjects:
                    pastStates = pastActGrp.mSubj2States[sPastSubject];
            return pastStates;
        #Get All Combinations of Past States (involving Subjects of this current time-step)
        for sPastSubject in aPastSubjects:
            self.mPast_Subj2States[sPastSubject] = getPastStates(sPastSubject);            
            nPastSt = len(self.mPast_Subj2States[sPastSubject]);
            if nPastSt>0:
                aPastBase.append(nPastSt);
                nPastCombs *= aPastBase[-1];
        if aPastBase==[]:
            nTransProd = self.getTransProd(self.aSubjects, aStates_Inds, [], []);
            return [nTransProd, []];
        #Get Max_TransFwd
        for i in range(nPastCombs):
            aPastStates_Inds = self.getPermutation(i, aPastBase); 
            bInconsistent = False;
#             nInconsistents = 0;
            for pastActGrp in self.aPastActGrps:
                aSubj, aSubjSt = [], [];
                for i,subject in enumerate(aPastSubjects):
                    if subject in pastActGrp.aSubjects:
                        aSubj.append(subject);
                        aSubjSt.append(aPastStates_Inds[i]);
                if len(aSubj)>1 and pastActGrp.check_Sub_Inconsistency(aSubj, aSubjSt):
#                     nInconsistents += 1;
                    bInconsistent = True;
                    break;
            if bInconsistent: continue;
#             nPastFwd, nTransProd = 0, 0;
#             if nInconsistents==0:
#                 nPastFwd = self.getPastFwd(aPastStates_Inds);
#                 nTransProd = self.getTransProd(self.aSubjects, aStates_Inds, aPastSubj, aPastStates_Inds);
#                 print "Inconsistent nPastFwd:", nPastFwd, nTransProd, nMax_TransFwd; 
#                 continue;
#                 nPastFwd = self.getPastFwd(aPastStates_Inds);
            nPastFwd = self.getPastFwd(aSubj, aPastStates_Inds);
            nTransProd = self.getTransProd(self.aSubjects, aStates_Inds, aPastSubjects, aPastStates_Inds);
#             nPastFwd = self.getPastFwd(aPastStates_Inds);
#             nTransFwd = nTransProd * nPastFwd;
            nTransFwd = nTransProd + nPastFwd;
            if nTransFwd>nMax_TransFwd:
                nMax_TransFwd = nTransFwd;
                aMaxFwd_PastStates_Inds = aPastStates_Inds;
        return [nMax_TransFwd, aMaxFwd_PastStates_Inds];
    
    def getPastFwd(self, aSubj, aPastStates_Inds):
        nPastFwd = 0; #1;
        for pastActGrp in self.aPastActGrps:
#             nPastFwd *= pastActGrp.getMarginal_PastFwd(self.aSubjects, aPastStates_Inds);
            nPastFwd += pastActGrp.getMarginal_PastFwd(aSubj, aPastStates_Inds);
        return nPastFwd;
    
# #     def getTransProd(self, aSubjects, aStates_Inds, aPastSubj, aPastStates_Inds):
# #         #Get Product of Subject's CPTs (for each past State's combinations)
# #         nTrans = 0; #1;
# #         for i,subject in enumerate(aSubjects): #For each Current Subject (each with their given state)
# # #             nTrans *= getCPT(iSubj, aStates_Inds[iSubj], aPastStates_Inds);
# # #             nCPT = getCPT(subject, aStates_Inds[i], aPastSubj, aPastStates_Inds);            
# #             state_ind = aStates_Inds[i];
# #             stateNode = self.mSubj2States[subject][state_ind];
# #             nodeInfoCPT = self.graph.ndict[stateNode.rootAct]; #CPT
# #             aPastStates = [];
# #             for iSubj,iPastSubj_State in enumerate(aPastStates_Inds):
# #                 sPastSubj = aPastSubj[iSubj];
# #                 aPastStates.append(self.mPast_Subj2States[sPastSubj][iPastSubj_State]);
# #             aBin = [0]*len(nodeInfoCPT[0]);
# #             if aBin==[]: #Get Prior (if it Doesn't have Parents)
# #                 nProbCPT = nodeInfoCPT[1][0];
# #             else: #Find CPT with linked Parents 
# #                 for pastSt in aPastStates:
# #                     if pastSt.rootAct in nodeInfoCPT[0]:
# #                         iPast = nodeInfoCPT[0].index(pastSt.rootAct);
# #                         aBin[iPast] = 1;
# #                 sBin = str(aBin).translate(None, "[], ");
# #                 nBin = int(sBin,2);
# #                 nProbCPT = nodeInfoCPT[1][nBin];
# #             nCPT = nProbCPT;
# #             
# #             nCPT = math.log(nCPT) if nCPT>0 else float("-inf"); #Log-Likelihoods
# #             nTrans += nCPT;
# #         return nTrans;
    
    def getTransProd(self, aSubjects, aStates_Inds, aPastSubj, aPastStates_Inds):
        def getCPT(subject, state_ind, aPastSubj, aPastStates_Inds):
            stateNode = self.mSubj2States[subject][state_ind];
            nodeInfoCPT = self.graph.ndict[stateNode.rootAct]; #CPT
            aPastStates = [];
            for iSubj,iPastSubj_State in enumerate(aPastStates_Inds):
                sPastSubj = aPastSubj[iSubj];
                aPastStates.append(self.mPast_Subj2States[sPastSubj][iPastSubj_State].rootAct);
            aBin = [0]*len(nodeInfoCPT[0]);
            if aBin==[]: #Get Prior (if it Doesn't have Parents)
                nProbCPT = nodeInfoCPT[1][0];
            else: #Find CPT with linked Parents
                for i,pastSt in enumerate(nodeInfoCPT[0]):
                    if pastSt in aPastStates:
                        aBin[i] = 1;
#                 for pastSt in aPastStates:
#                     if pastSt.rootAct in nodeInfoCPT[0]:
#                         iPast = nodeInfoCPT[0].index(pastSt.rootAct);
#                         aBin[iPast] = 1;
#                 nBin = self.getIndex(aBin, [2]*len(nodeInfoCPT[0]));
                sBin = str(aBin).translate(None, "[], ");
#                 sBin = str(aBin).replace("[","").replace("]","").replace(", ", "");
                nBin = int(sBin,2);
                nProbCPT = nodeInfoCPT[1][nBin];
            return nProbCPT;
        #Get Product of Subject's CPTs (for each past State's combinations)
        nTrans = 0; #1;
        for i,subject in enumerate(aSubjects): #For each Current Subject (each with their given state)
#             nTrans *= getCPT(iSubj, aStates_Inds[iSubj], aPastStates_Inds);
            nCPT = getCPT(subject, aStates_Inds[i], aPastSubj, aPastStates_Inds);
            nCPT = math.log(nCPT) if nCPT>0 else float("-inf"); #Log-Likelihoods
            nTrans += nCPT;
        return nTrans;
    
    def getBestStates(self):        
        aBest_States = [-1]*len(self.aSubjects);
        aBest_States_Temp = [-1]*len(self.aSubjects);
        nMaxFwd = float("-inf");
#         def setAll_BestStates(aBest_States):
#             for i,iBestState in enumerate(aBest_States): #Set All Best Subject's States
#                 subject = self.aSubjects[i];
#                 self.aBest_Subj2States[subject] = iBestState;
#             aBestPast_States = self.aMaxFwd_PastInds[tuple(aBest_States)];
#             for actGrp in self.aPastActGrps: #Set Past Best Subject's States (Based on best Subject's States found earlier)
#                 for pastSubject in actGrp.aSubjects:
#                     if pastSubject in self.aSubjects:
#                         iSubj = self.aSubjects.index(pastSubject);
#                         actGrp.aBest_Subj2States[pastSubject] = aBestPast_States[iSubj];
        #If None Subject has Best State, then find All of them
        if len(self.aBest_Subj2States)==0:
            for subjStates in self.aJntFwd:
                if self.aJntFwd[subjStates] > nMaxFwd:
                    nMaxFwd = self.aJntFwd[subjStates];
                    aBest_States = list(subjStates);
            self.setAll_BestStates(aBest_States);
            return self.aBest_Subj2States;            
        #Else If there are some Subjects with Best States, then find the remaining Missing Subject's Best States 
        elif len(self.aBest_Subj2States)<len(self.aSubjects):
            aMissingSubj_Inds, aMissingBases, nMissingComb = [], [], 1;
            for i,subject in enumerate(self.aSubjects): #Get Subject's Best States & Info about Missing Subject's Best States
                if self.aBest_Subj2States.has_key(subject): #Fixing Subject's Best States (given from time "t+1")
                    aBest_States_Temp[i] = self.aBest_Subj2States[subject];
                else: #Getting Info about Missing Subject's Best States
                    aMissingSubj_Inds.append(i);
                    aMissingBases.append(self.aBase[i]);
                    nMissingComb *= self.aBase[i];
            for i in range(nMissingComb): #Find the remaining Missing Subject's Best States
                aMissingSt_Inds = self.getPermutation(i, aMissingBases);
                for i,iSubj in enumerate(aMissingSubj_Inds):
                    aBest_States_Temp[iSubj] = aMissingSt_Inds[i];
                if not(self.aJntFwd.has_key(tuple(aBest_States_Temp))): continue; #If is Inconsistent, then try another permutation
                if self.aJntFwd[tuple(aBest_States_Temp)] > nMaxFwd:
                    nMaxFwd = self.aJntFwd[tuple(aBest_States_Temp)];
                    aBest_States = aBest_States_Temp;
            self.setAll_BestStates(aBest_States);
        #Else If All Subjects have Best States, then set them and backpropagate them to past States 
        elif len(self.aBest_Subj2States)==len(self.aSubjects):
            aBest_States = [];
            for subject in self.aSubjects:
                aBest_States.append(self.aBest_Subj2States[subject]);
            if not(self.check_Inconsistency(aBest_States)):
                self.setAll_BestStates(aBest_States);
        return self.aBest_Subj2States;
    
    def setAll_BestStates(self, aBest_States):
        for i,iBestState in enumerate(aBest_States): #Set All Best Subject's States
            subject = self.aSubjects[i];
            self.aBest_Subj2States[subject] = iBestState;
        aBestPast_States = self.aMaxFwd_PastInds[tuple(aBest_States)];
        for i,pastSubject in enumerate(self.aPastSubjects): #Set Past Best Subject's States (Based on best Subject's States found earlier)
            for pastActGrp in self.aPastActGrps:
                if pastSubject in pastActGrp.aSubjects:
                    pastActGrp.aBest_Subj2States[pastSubject] = aBestPast_States[i];
        
#         for actGrp in self.aPastActGrps: #Set Past Best Subject's States (Based on best Subject's States found earlier)
#             for pastSubject in actGrp.aSubjects:
#                 if pastSubject in self.aSubjects:
#                     iSubj = self.aSubjects.index(pastSubject);
#                     actGrp.aBest_Subj2States[pastSubject] = aBestPast_States[iSubj];
    
    #Transform from Permutation to Index
    def getIndex(self, aPermutation, aBase):
        nIndex, aIndex = 0, range(len(aBase)); #aIndex = [iLocBase1, iLocBase2, ..., iLocBaseN]
        for i, nV in enumerate(aPermutation):
            nF, nBases = 1, len(aBase[i+1:]);
            for j in range(nBases):
                nF *= aBase[aIndex[j+1]];
            nIndex += (nV*nF);
        return nIndex;
    
    #Transform from Index to Permutation
    def getPermutation(self, nIndex, aBase):
        aVals = [];
        for nB in aBase[::-1]:
            aVals.append(nIndex % nB);
            nIndex /= nB;
        aVals.reverse();
        return aVals;
    
    def getConsistentStates(self):
        #Get Consistent States
#         aStateSpace = self.getStateSpace(aSortedHmms, sTime);
        aChosenStates, aChosenTuple, aSamples, aSampleTuples = [], [], [], [];
        if len(self.aSubjects)==1:
            for i in range(len(self.mSubj2States[self.aSubjects[0]])):
                aSampleTuples.append(tuple([i]));
            return aSampleTuples;
        nMaxSamples = float("inf");
        aRootSt = [];        
        #Get Root level (of Choice Tree), by adding all Children
        sFirstSubject = self.aSubjects[0];
        for childSt in self.mSubj2States[sFirstSubject]:
            aRootSt.append(childSt.rootAct);
        #Get N samples of Consistent State Assignments
        while aRootSt!=[] and len(aSamples)<nMaxSamples:
            for subject in self.aSubjects:
                #Get Candidates of this State
                if aChosenStates==[]: #If it's Root Level, then get current root candidate states
                    aChildStates = aRootSt;
                else: #Else, choose among Candidates (Children) of selected State
                    parentSt = aChosenStates[-2] if len(aChosenStates)>=2 else None;
                    tKey = self.getChosenIds(aChosenStates); #(aSortedHmms[i-1].sArgSubject, parentSt);
                    if not(aChosenStates[-1].mChildren.has_key(tKey)):
                        #Initialize & Get Children of last Chosen State
                        aSubjStates = self.mSubj2States[subject];
                        aChildStates = self.getChildStates(subject, aSubjStates, aChosenStates);
                        aChosenStates[-1].mChildren[tKey] = aChildStates;
                    else: #Get current Children of last Chosen State
                        aChildStates = aChosenStates[-1].mChildren[(tKey)];
                #Pick Any Random Child State
#                 iRnd = random.randrange(len(aChildStates)); #Stochastic
                iRnd = 0; #Deterministic
                aChosenStates.append(aChildStates[iRnd]);
                
                #If leaf reached (if it's last subject), then mark this choice
                if subject==self.aSubjects[-1]:
                    #Remove Leaf from Last Chosen State's Children
                    iP, iCh = 2, 1;
                    parentSt = aChosenStates[-iP] if len(aChosenStates)>=iP else None;
                    tKey = self.getChosenIds(aChosenStates[:-1]);
                    iDel = parentSt.mChildren[tKey].index(aChosenStates[-iCh]);
                    del parentSt.mChildren[tKey][iDel];
                    #If that was Last Child, then Go Backwards removing Last Chosen State's Children
                    while parentSt!=None and len(parentSt.mChildren[tKey])==0:
                        iP += 1;
                        iCh = iP-1;
                        parentSt = aChosenStates[-iP] if len(aChosenStates)>=iP else None;
                        if parentSt==None:
                            iDel = aRootSt.index(aChosenStates[0]);
                            del aRootSt[iDel];
                        else:
                            tKey = self.getChosenIds(aChosenStates[:-iP+1]);
                            iDel = parentSt.mChildren[tKey].index(aChosenStates[-iCh]);
                            del parentSt.mChildren[tKey][iDel];
            aSamples.append(aChosenStates);
            #Print Sample for Debugging
            sChosenStates = [];
            aChosenTuple = [];
            for i,chosenSt in enumerate(aChosenStates):
                sChosenStates.append(chosenSt.name);
                subject = self.aSubjects[i];
                aChosenTuple.append(self.mSubj2StNames[subject].index(chosenSt.name));
#             print "Adding Sample "+str(len(aSamples))+":", tuple(aChosenTuple), sChosenStates;
            aChosenStates = [];
            aSampleTuples.append(tuple(aChosenTuple));
        return aSampleTuples; #aSamples;
    
    def getChosenIds(self, aChosenStates):
        sChosenId = "";
        for chosenSt in aChosenStates:
            sChosenId += chosenSt.sId;
        return sChosenId;
            
    def getChildStates(self, subject, aSubjStates, aChosenStates):
        aChildStates, multSt_And_This = [], None;
        #If There is Any [Multiple] State that Include this Subject, then Choose such state
        for chosenSt in aChosenStates:
            if subject in chosenSt.aSubjects:
                multSt_And_This = chosenSt;
        if multSt_And_This!=None:
            #Pick such Child State (that has this same Subject)
            aChildStates = [multSt_And_This];
        #Else, then choose either Single or any Multiple State NOT involving last subjects
        elif multSt_And_This==None:
            #Get Candidate [Children] states NOT involving last subjects
            for childSt in aSubjStates:
                bInLastSubjects = False;
                for sArg in childSt.aSubjects: #For each Child's Subject
                    if bInLastSubjects: break;
                    for lastSt in aChosenStates: #Check each last State's subjects
                        if sArg in lastSt.aSubjects:
                            bInLastSubjects = True;
                            break;
                if not(bInLastSubjects):
                    aChildStates.append(childSt.rootAct);
        return aChildStates;